package com.shopping.mall.core.module.user.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.shopping.mall.auth.VO.EnterpriseVo;
import com.shopping.mall.auth.VO.UserVo;
import com.shopping.mall.auth.exception.RedisTokenErrorException;
import com.shopping.mall.auth.exception.RedisTokenNullException;
import com.shopping.mall.auth.utils.JwtUtils;
import com.shopping.mall.auth.utils.UserUtils;
import com.shopping.mall.base.redis.RedisHeader;
import com.shopping.mall.core.module.goods.service.impl.GoodsServiceImpl;
import com.shopping.mall.core.util.MailUtils;
import com.shopping.mall.core.module.enterprise.entity.Enterprise;
import com.shopping.mall.core.module.user.entity.Role;
import com.shopping.mall.core.module.user.entity.User;
import com.shopping.mall.core.module.user.mapper.UserMapper;
import com.shopping.mall.core.module.user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.shopping.mall.base.http.ResponseState;
import com.shopping.mall.base.http.Result;
import com.shopping.mall.base.util.WrapperUtils;
import io.jsonwebtoken.ExpiredJwtException;
import org.slf4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * User服务实现类
 * </p>
 *
 * @author zzy
 * @since 2021-07-27
 */
@Validated
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private Logger logger;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private MailUtils mailUtils;

    /**
     * 发送邮箱验证码
     * 先查询数据库判断邮箱是否已注册,未注册的话则返回发送中的信息和异步发邮件
     *
     * @param mailToAddress : 邮件的接收地址
     * @return Result<Object>
     */
    @Override
    public Result<Object> registerSendMail(String mailToAddress) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mail", mailToAddress);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            //异步方法
            mailUtils.sendRegisterMail(mailToAddress);
            logger.info("验证码发送中");
            return Result.OK();
        } else {
            logger.info("该邮箱已注册");
            return Result.error(ResponseState.MAIL_IS_EXIST.getValue(), ResponseState.MAIL_IS_EXIST.getMessage());
        }
    }

    /**
     * 注册
     * 注册成功后删除redis缓存里的验证码
     *
     * @param userVo: 注册信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> register(UserVo userVo) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mail", userVo.getMail().toLowerCase(Locale.ROOT));
        User selectOne = userMapper.selectOne(queryWrapper);
        if (selectOne != null) {
            logger.info("该邮箱已注册");
            return Result.error(ResponseState.MAIL_IS_EXIST.getValue(), ResponseState.MAIL_IS_EXIST.getMessage());
        }
        User user = new User();
        user.setPassword(passwordEncoder.encode(userVo.getPassword()));
        user.setMail(userVo.getMail().toLowerCase(Locale.ROOT));
        user.setRoleId(1);
        userMapper.insert(user);
        return Result.OK();
    }

    /**
     * 登录
     *
     * @param userVo : 登录信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> login(UserVo userVo) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mail", userVo.getMail().toLowerCase(Locale.ROOT));
        User selectUser = userMapper.selectOne(queryWrapper);
        if (selectUser == null) {
            logger.info("用户不存在,检查用户名或密码是否正确");
            return Result.error(ResponseState.PASSWORD_IS_ERROR.getValue(), ResponseState.PASSWORD_IS_ERROR.getMessage());
        }
        if (!passwordEncoder.matches(userVo.getPassword(), selectUser.getPassword())) {
            logger.info("密码错误");
            return Result.error(ResponseState.PASSWORD_IS_ERROR.getValue(), ResponseState.PASSWORD_IS_ERROR.getMessage());
        } else {
            logger.info("密码正确");
            //返回token
            if (selectUser.getRoleId() == 1) {
                selectUser.setRole(Role.NORMAL_USER);
            }
            if (selectUser.getRoleId() == 2) {
                selectUser.setRole(Role.ENTERPRISE_USER);
            }
            String accessToken = jwtUtils.getAccessToken(selectUser.getId(), selectUser.getRole().getRole());
            String refreshToken = jwtUtils.getRefreshToken(selectUser.getId(), selectUser.getRole().getRole());
            stringRedisTemplate.opsForValue().set(RedisHeader.TOKEN.getHeader() + selectUser.getId(), accessToken, 24 * 3600L, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set(RedisHeader.REFRESH_TOKEN.getHeader() + selectUser.getId(), refreshToken, 15 * 24 * 3600L, TimeUnit.SECONDS);
            selectUser.setPassword(null);
            Map<String, Object> data = new HashMap<>();
            data.put("token", accessToken);
            data.put("refreshToken", refreshToken);
            data.put("user", selectUser);
            return Result.OK(data);
        }
    }

    /**
     * 查看个人信息
     *
     * @return
     */
    @Override
    public Result<Object> userInfo() {
        final String loginUserId = UserUtils.getLoginUserId();
        final User user = userMapper.selectById(loginUserId);
        if (user != null) {
            user.setPassword(null);
            return Result.OK(user);
        } else {
            return Result.error(ResponseState.USER_NOT_EXIST.getValue(), ResponseState.USER_NOT_EXIST.getMessage());
        }
    }

    /**
     * 退出登录
     *
     * @return Result<Object>
     */
    @Override
    public Result<Object> logout() {
        String loginUserId = UserUtils.getLoginUserId();
        stringRedisTemplate.delete(RedisHeader.TOKEN.getHeader() + loginUserId);
        stringRedisTemplate.delete(RedisHeader.REFRESH_TOKEN.getHeader() + loginUserId);
        logger.info("退出登录,redis token已删除");
        return Result.OK();
    }

    /**
     * 刷新token
     *
     * @param refreshToken : refreshToken
     * @return Result<Object>
     */
    @Override
    public Result<Object> refreshToken(String refreshToken) {
        try {
            final String id = jwtUtils.getUserId(refreshToken);
            final String role = jwtUtils.getUserRole(refreshToken);
            final String newAccessToken = jwtUtils.getAccessToken(id, role);
            final String newRefreshToken = jwtUtils.getRefreshToken(id, role);
            stringRedisTemplate.opsForValue().set(RedisHeader.TOKEN.getHeader() + id, newAccessToken, 24 * 3600L, TimeUnit.SECONDS);
            stringRedisTemplate.opsForValue().set(RedisHeader.REFRESH_TOKEN.getHeader() + id, newRefreshToken, 15 * 24 * 3600L, TimeUnit.SECONDS);
            final User user = userMapper.selectById(id);
            user.setPassword(null);
            logger.info("token刷新成功");
            Map<String, Object> data = new HashMap<>();
            data.put("token", newAccessToken);
            data.put("refreshToken", refreshToken);
            data.put("user", user);
            return Result.OK(data);
        } catch (RedisTokenErrorException e) {
            logger.warn("refreshToken错误:{}", e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_ERROR.getValue(), ResponseState.REFRESH_TOKEN_IS_ERROR.getMessage());
        } catch (RedisTokenNullException | ExpiredJwtException e) {
            logger.warn("refreshToken已过期:{}", e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_EXPIRED.getValue(), ResponseState.REFRESH_TOKEN_IS_EXPIRED.getMessage());
        } catch (Exception e) {
            logger.error("发生错误:{}", e.getStackTrace());
            return Result.error(ResponseState.REFRESH_TOKEN_IS_ERROR.getMessage(), ResponseState.REFRESH_TOKEN_IS_ERROR.getValue());
        }
    }

    /**
     * 修改密码
     *
     * @param userVo : 用户信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> changePassword(UserVo userVo) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mail", userVo.getMail().toLowerCase(Locale.ROOT));
        User selectUser = userMapper.selectOne(queryWrapper);
        if (selectUser == null) {
            return Result.error(ResponseState.USER_NOT_EXIST.getValue(), ResponseState.USER_NOT_EXIST.getMessage());
        }
        if (passwordEncoder.matches(userVo.getPassword(), selectUser.getPassword())) {
            selectUser.setPassword(passwordEncoder.encode(userVo.getChangedPassword()));
            userMapper.updateById(selectUser);
            return Result.OK();
        } else {
            return Result.error(ResponseState.PASSWORD_IS_ERROR.getValue(), ResponseState.PASSWORD_IS_ERROR.getMessage());
        }
    }

    /**
     * 修改个人信息
     *
     * @param userVo : 用户信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> changeUserInfo(UserVo userVo) {
        User user = new User();
        BeanUtils.copyProperties(userVo, user);
        final String id = UserUtils.getLoginUserId();
        user.setId(id);
        final int i = userMapper.updateById(user);
        logger.info("用户信息更新,有{}行被修改", i);
        return Result.OK();
    }

    /**
     * 发送忘记密码验证码
     *
     * @return Result<Object>
     * @param email
     */
    @Override
    public Result<Object> sendForgetMail(String email) {
        final String loginUserId = UserUtils.getLoginUserId();
        try {
            mailUtils.sendForgetMail(email.toLowerCase(Locale.ROOT));
            logger.info("验证码发送中");
            return Result.OK();

        } catch (Exception e) {
            logger.error("用户不存在:{}", e.getStackTrace());
            return Result.error(ResponseState.USER_NOT_EXIST.getMessage());
        }
    }

    /**
     * 发送换绑时解除邮箱绑定验证码
     *
     * @return Result<Object>
     */
    @Override
    public Result<Object> sendUnbindMail() {
        final String userId = UserUtils.getLoginUserId();
        try {
            final String mail = userMapper.selectById(userId).getMail().toLowerCase(Locale.ROOT);
            mailUtils.sendUnbindMail(mail);
            logger.info("验证码发送中");
            return Result.OK();
        } catch (Exception e) {
            logger.error("用户不存在:{}", e.getStackTrace());
            return Result.error(ResponseState.USER_NOT_EXIST.getValue(), ResponseState.USER_NOT_EXIST.getMessage());
        }

    }

    /**
     * 发送绑定新邮箱验证码
     *
     * @param newMail : 邮件的接收地址
     * @return Result<Object>
     */
    @Override
    public Result<Object> sendBindMail(String newMail) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("mail", newMail.toLowerCase(Locale.ROOT));
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
            mailUtils.sendBindMail(newMail.toLowerCase(Locale.ROOT));
            logger.info("验证码发送中");
            return Result.OK();
        } else {
            logger.info("该邮箱已注册");
            return Result.error(ResponseState.MAIL_IS_EXIST.getValue(), ResponseState.MAIL_IS_EXIST.getMessage());
        }
    }

    /**
     * 邮箱换绑
     *
     * @param newMail:   新的邮箱地址
     * @param unBindCode : 解绑验证码
     * @param bindCode   : 绑定验证码
     * @return Result<Object>
     */
    @Override
    public Result<Object> changeBindMail(String newMail, String unBindCode, String bindCode) {
        final String userId = UserUtils.getLoginUserId();
        try {
            final User user = userMapper.selectById(userId);
            final String rightUnBindCode = stringRedisTemplate.opsForValue().get(RedisHeader.MAIL_UNBIND_CODE.getHeader() + user.getMail().toLowerCase(Locale.ROOT));
            final String rightBindCode = stringRedisTemplate.opsForValue().get(RedisHeader.MAIL_BIND_CODE.getHeader() + newMail);
            if (unBindCode.equals(rightUnBindCode) && bindCode.equals(rightBindCode)) {
                stringRedisTemplate.delete(RedisHeader.MAIL_UNBIND_CODE.getHeader() + user.getMail().toLowerCase(Locale.ROOT));
                stringRedisTemplate.delete(RedisHeader.MAIL_BIND_CODE.getHeader() + newMail.toLowerCase(Locale.ROOT));
                user.setMail(newMail.toLowerCase(Locale.ROOT));
                userMapper.updateById(user);
                logger.info("邮箱修改成功");
                return Result.OK();
            } else {
                logger.error("验证码错误");
                return Result.error(ResponseState.CODE_IS_ERROR.getValue(), ResponseState.CODE_IS_ERROR.getMessage());
            }
        } catch (Exception e) {
            logger.error("用户不存在{}", e.getStackTrace());
            return Result.error(ResponseState.USER_NOT_EXIST.getValue(), ResponseState.USER_NOT_EXIST.getMessage());
        }
    }

    /**
     * 忘记密码时的重置密码,需要邮箱验证码校验
     *
     * @param userVo: 用户信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> resetPassword(UserVo userVo) {
        final String changedPassword = userVo.getChangedPassword();
        final String code = userVo.getCode();
//        final String loginUserId = UserUtils.getLoginUserId();
        try {
            final User user = userMapper.selectOne(WrapperUtils.getQueryWrapper("mail",userVo.getMail().toLowerCase(Locale.ROOT)));
            final String key = RedisHeader.FORGET_PASSWORD_CODE.getHeader() + user.getMail().toLowerCase(Locale.ROOT);
            final String authCode = stringRedisTemplate.opsForValue().get(key);
            if (code.equals(authCode)) {
                user.setPassword(passwordEncoder.encode(changedPassword));
                final int i = userMapper.updateById(user);
                stringRedisTemplate.delete(key);
                logger.info("验证码正确,密码重置成功,有{}行被修改", i);
                return Result.OK();
            } else {
                logger.info("验证码错误");
                return Result.error(ResponseState.CODE_IS_ERROR.getValue(), ResponseState.CODE_IS_ERROR.getMessage());
            }
        } catch (Exception e) {
            logger.error("用户不存在:{}", e.getStackTrace());
            return Result.error(ResponseState.USER_NOT_EXIST.getMessage());
        }
    }

    /**
     * 申请成为企业用户
     *
     * @param enterpriseVo: 企业用户表
     * @return Result<Object>
     */
    @Override
    public Result<Object> applyEnterpriseUser(EnterpriseVo enterpriseVo) {
        final String id = UserUtils.getLoginUserId();
        Enterprise enterprise = new Enterprise();
        BeanUtils.copyProperties(enterpriseVo, enterprise);
        enterprise.setStatus(0);
        enterprise.setId(id);
        return null;
    }

    /**
     * 分页查询
     *
     * @param current：页码
     * @param size：每页条数
     * @param user：筛选条件
     * @return IPage<User>
     */
    @Override
    public IPage<User> getUserPage(Integer current, Integer size, User user) {
        QueryWrapper<User> queryWrapper = WrapperUtils.queryWrapper(user);
        Page<User> userPage = new Page<>(current, size);
        IPage<User> userIPage = userMapper.selectPage(userPage, queryWrapper);
        return userIPage;
    }

    /**
     * 查询所有
     *
     * @param user：筛选条件
     * @return List<User>
     */
    @Override
    public List<User> getAll(User user) {
        QueryWrapper<User> queryWrapper = WrapperUtils.queryWrapper(user);
        return userMapper.selectList(queryWrapper);
    }

    /**
     * 通过id删除
     *
     * @param id：userId
     * @return Result<Object>
     */
    @Override
    public Result<Object> deleteById(String id) {
        User user = userMapper.selectById(id);
        if (user == null) {
            return Result.error(ResponseState.TARGET_NOT_EXIST.getValue(), ResponseState.TARGET_NOT_EXIST.getMessage());
        }
        user.setIsDeleted(1);
        userMapper.updateById(user);
        return Result.OK();
    }

    /**
     * 批量删除
     *
     * @param ids：id列表
     * @return Result<Object>
     */
    @Override
    public Result<Object> batchDelete(List<String> ids) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.in("id", ids);
        userMapper.delete(updateWrapper);
        return Result.OK();
    }
}